#include "widget.h"
#include "ui_widget.h"
#include <QMessageBox>
#include <QTimer>
#include <QTime>
#include <QDebug>
#include <QPalette> //可改变界面颜色
#include <QtWidgets>
#include "arm_jsoncpp/json.h"
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<sys/time.h>
#include<errno.h>
#include<sys/ioctl.h>
#include<sys/select.h>
/*
作者：阿正
项目名称：智慧物流场景案例实战开发
基于STM32MP157 cat.1 QT GPS MQTT HUAWEI-IOT
参考资料：来源于网络
制作日期：2020年8月25日
功能：实现i2C传感器数据采集与GPS数据获取，并通过MQTT上报数据到华为云平台，且在华为云配置Web界面展示定位数据与设备信息

博客：http://wenzheng.club
*/
#define Address                                     0x44                  //SHT30地址
#define I2C_RETRIES                                 0x0701
#define I2C_TIMEOUT                                 0x0702
#define I2C_SLAVE                                   0x0703                //IIC从器件的地址设置
#define I2C_BUS_MODE                                0x0780

unsigned int msg_flag =0;
int fd = -1;
double temperature;
double humidity;
quint8 testbuf[100];
QString receive_str;
QProcess *process = new QProcess(); //开启新线程，调用MQTT

//开启新线程执行系统命令 需要这个头文件：#include <QtWidgets>

Widget::Widget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Widget)
{
    ui->setupUi(this);
    //设置背景颜色
    palette.setColor(QPalette::Background,QColor(105,105,105));
    this->setPalette(palette);

    timer = new QTimer(this);                                          //配置定时器
    SHT30_timer = new QTimer(this);                                    //配置定时器
    //关联槽函数
    connect(timer,SIGNAL(timeout()),this,SLOT(gpsparse()));            //GPS 数据采集定时任务
    connect(SHT30_timer,SIGNAL(timeout()),this,SLOT(SHT30_Get_data()));//SHT30 数据采集定时任务
    connect(process,SIGNAL(finished(int,QProcess::ExitStatus)),this,SLOT(finishProcess(int,QProcess::ExitStatus))); //关联进程槽函数
    setWindowTitle("智慧物流场景");
    serialport = new QSerialPort;
    find_port();                                                       //查找可用串口
    SHT30_Init();                                                      //SHT30 初始化
    SHT30_timer->start(1000);                                          //开启SHT30定时器
}

Widget::~Widget()
{
    delete ui;
    delete timer;
    delete SHT30_timer;
}

//查找串口
void Widget::find_port()
{
    //查找可用的串口
    //ui->com->clear();
    foreach(const QSerialPortInfo &info, QSerialPortInfo::availablePorts())
    {
        QSerialPort serial;
        serial.setPort(info);                                        //设置串口
        if(serial.open(QIODevice::ReadWrite))
        {
            ui->com->addItem(serial.portName());                     //在控件中添加串口名称
            serial.close();
        }
    }
}
//打开串口
void Widget::on_open_btn_clicked()
{
    update();
    sleep(100);      //延时100ms
    //find_port();   //重新查找com
    //初始化串口
    serialport->setPortName(ui->com->currentText());                  //设置串口名
    if(serialport->open(QIODevice::ReadWrite))                        //打开串口成功
    {
        serialport->setBaudRate(ui->baud->currentText().toInt());     //设置波特率
        serialport->setDataBits(QSerialPort::Data8);                  //设置数据位数
        serialport->setParity(QSerialPort::NoParity);                 //设置奇偶校验
        serialport->setStopBits(QSerialPort::OneStop);                //设置停止位
        serialport->setFlowControl(QSerialPort::NoFlowControl);       //设置流控制
        QObject::connect(serialport, &QSerialPort::readyRead, this, &Widget::Read_Date);//连接槽函数
        // 设置控件可否使用
        ui->open_btn->setEnabled(false);
        ui->close_btn->setEnabled(true);
        timer->start(2000);  //开启串口数据解析定时器
    }
    else    //打开失败提示
    {
        sleep(100);
        QMessageBox::information(this,tr("Erro"),tr("Open the failure"),QMessageBox::Ok);
    }
}
//窗口显示串口传来的数据
void Widget::Read_Date()
{
    QByteArray buf;
    buf = serialport->readAll();
    if(!buf.isEmpty())          //将数据显示到文本串口
    {
        receive_str = ui->Receive_text_window->toPlainText(); //获取上次的文本
        receive_str+=tr(buf);
        ui->Receive_text_window->clear();                     //把上次的清空
        ui->Receive_text_window->append(receive_str);         //添加文本
    }else
    {
        ui->Receive_text_window->clear();
    }
    buf.clear();    //清空缓存区
}
//  $GNRMC,000400.028,V,3445.8715,N,11337.8655,E,0.000,0.00,060180,,,N*5D
// 经度 Longitude 纬度 latitude
//定时处理串口数据
void Widget::gpsparse()
{
    if(  receive_str.contains("*") && receive_str.contains("RMC"))
    {
        QStringList gpsByteArrays = receive_str.split("\n"); //以结尾符分割
        int count = gpsByteArrays.count();
        QString RMC_str,GGA_str;
        ui->debug_text->clear();
        for(int i =0;i<count;i++)  //遍历数据，查找定位信息
        {
            receive_str = gpsByteArrays.at(i);
            //qDebug()<< receive_str; //打印遍历数据
            if( receive_str.contains("RMC"))
            {
                RMC_str =   receive_str  ;
            }else if(receive_str.contains("GGA"))
            {
                GGA_str =  receive_str ;
            }

        }
        QStringList RMCArrays = RMC_str.split(','); //以逗号符分割
        QStringList GGAArrays = GGA_str.split(','); //以逗号符分割
        // qDebug()<< RMCArrays.at(3)<< RMCArrays.at(5) ;
        int  gpsLat_1 = static_cast<int>(RMCArrays.at(3).toDouble()/100);
        double gpsLat_2 = (RMCArrays.at(3).toDouble() - gpsLat_1 * 100)/60;
        double gpslat=gpsLat_1 + gpsLat_2;
        int gpsLong_1 = static_cast<int>(RMCArrays.at(5).toDouble()/100);
        double gpsLong_2 = (RMCArrays.at(5).toDouble()-gpsLong_1 * 100)/60;
        double gpsLong = gpsLong_1 + gpsLong_2;
        //qDebug() << QString::number(gpsLat_1, 'f', 4);  //double小数点后真实存在，只是显示问题
        if(RMC_str.contains("GNRMC")) ui->locate_mode->setText("GNSS系统");
        if(RMC_str.contains("GPRMC")) ui->locate_mode->setText("GPS系统");
        if(RMC_str.contains("BDRMC")) ui->locate_mode->setText("北斗系统");
        if(RMC_str.contains("GLRMC")) ui->locate_mode->setText("GLONASS系统");
        if(RMC_str.contains("GARMC")) ui->locate_mode->setText("Galileo系统");
        ui->satellite_number->setText(QString::number(GGAArrays.at(8).toInt())); //显示卫星数量
        ui->latitude_text->setText(QString::number(gpslat));   //显示纬度
        ui->Longitude_text->setText(QString::number(gpsLong)); //显示经度
        ui->debug_text->append(RMC_str); //
        ui->debug_text->append(GGA_str);
        ui->Receive_text_window->clear(); //把上次的清空
        receive_str = " ";
        /*********************************json数据封装*******************************************/
        Json::Value root;
        Json::Value A;
        Json::Value B;
        Json::Value C;
        Json::FastWriter writer;

        B["Longitude"] = gpsLong;
        B["Latitude"] = gpslat;
        A["service_id"] = "Track";
        A["properties"]=B;
        root["services"].append(A);

        B["temperature"] = temperature;
        B["humidity"] = humidity;
        A["service_id"] = "Sensor";
        A["properties"]=B;
        root["services"].append(A);

        std::string json_file = writer.write(root);
        QString qstr = QString::fromStdString(json_file);   //std::string 转QString
        qDebug() << "mqtt--->start--->" << qstr;
        QString str_exe;
        QStringList args;
        str_exe = "./pub0sub1";       //相对于Qt执行程序的相对路径，也可以改成绝对路径
        args << qstr;                 //传递的参数，可以改成实时变化的
        process->start(str_exe,args); // 外部程序启动后，将随主程序的退出而退出。
        if(process->waitForStarted())
        {
            //qDebug() << "mqtt--->start--->" << qstr;
        }
    }
}
//进程结束的槽函数
void Widget::finishProcess(int, QProcess::ExitStatus exitStatus)
{
    if(exitStatus == QProcess::NormalExit)
    {
        // ui->Edit_tip->append("process exit normal");
        qDebug() << "MQTT process exit normal";
    }
    else
    {
        //ui->Edit_tip->append("process exit crash");
        qDebug() << "MQTT process exit crash";
    }
    //ui->Edit_tip->append("process finished");
    process->kill();
}

//延时函数
void Widget::sleep( int msec)
{
    QTime dieTime = QTime::currentTime().addMSecs(msec);
    while( QTime::currentTime() < dieTime )
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}

void Widget::on_close_btn_clicked()
{
    timer->stop();
    serialport->clear();        //清空缓存区
    serialport->close();        //关闭串口
    //使某些串口不可使用
    ui->open_btn->setEnabled(true);
    ui->close_btn->setEnabled(false);
}

//SHT30 初始化函数
int Widget::SHT30_Init(void)
{

    fd  = open("/dev/i2c-0", O_RDWR);               // open file and enable read and  write
    if (fd < 0)
    {
        perror("Can't open /dev/SHT30 \n");       // open i2c dev file fail
        return - 1;
    }
    printf("open /dev/i2c-0 success !\n");
    if (ioctl(fd, I2C_SLAVE, Address) < 0)
    {
        // set i2c address
        printf("fail to set i2c device slave address!\n");
        ::close(fd);
        return - 1;
    }
    printf("set slave address to 0x%x success!\n", Address);
    return fd;
}
//获取SHT30温湿度数据
int Widget::SHT30_Get_data(void)
{
    quint8 buf[8] = {0};
    buf[0] = 0x2C;
    buf[1] = 0x0D;
    if ( write(fd, buf, 2) != 2 ) {
        perror("faile to write config.\n");
        return -1;
    }
    buf[0] = 0;
    buf[1] = 0;
    if ( read(fd, buf, 6) != 6 ) {
        perror("faile to read back configure data.\n");
        return -1;
    }
    // printf("SHT30---->: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x \n", buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
    temperature = ((float)((buf[0]<<8)+buf[1]))*175/65535-45;
    humidity = ((float)((buf[3]<<8)+buf[4]))*100/65535;
    printf("%f.%f\r\n",temperature,humidity);
    ui->temperature_text->setText(QString::number(temperature));
    ui->humidity_text->setText(QString::number(humidity));
    return  0;
}

//CRC校验码计算
unsigned short CRC16(unsigned char *pBuf,unsigned short len)
{
    unsigned short uCRC=0xFFFF;
    int i,j;
    for(i=0;i<len;i++)
    {
        uCRC^=(*(pBuf+i));
        for(j=0;j<8;j++)
        {
            if((uCRC&0x0001)==0x0001)
            {
                uCRC=(uCRC>>1);
                uCRC^=0xA001;
            }
            else
                uCRC=(uCRC>>1);
        }
    }
    return uCRC;
}

void Widget::on_test_btn_clicked()
{

}
